#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
增强版微信支付服务
支持动态配置、防重支付、回调处理
"""

import hashlib
import hmac
import time
import xml.etree.ElementTree as ET
from urllib.parse import urlencode
import uuid
import requests
from datetime import datetime, timedelta
from decimal import Decimal
from flask import current_app
from app import db
from app.models.payment import Payment, PaymentLog
from app.models.payment_config import PaymentConfig
from app.models.order import Order


class WeChatPayEnhanced:
    """增强版微信支付类"""
    
    def __init__(self):
        self.config = None
        self.load_config()
    
    def load_config(self):
        """加载微信支付配置"""
        self.config = PaymentConfig.get_active_wechat_config()
        if not self.config:
            raise ValueError("未找到可用的微信支付配置")
        
        # 验证配置完整性
        is_valid, message = self.config.validate_wechat_config()
        if not is_valid:
            raise ValueError(f"微信支付配置不完整: {message}")
    
    def generate_payment_order(self, order_id, description=None, expire_minutes=30):
        """
        为订单生成支付二维码
        
        Args:
            order_id: 订单ID
            description: 支付描述
            expire_minutes: 支付过期时间(分钟)
            
        Returns:
            dict: 包含二维码URL和支付信息的字典
        """
        try:
            # 获取订单信息
            order = Order.query.get(order_id)
            if not order:
                raise ValueError("订单不存在")
            
            if order.status != 0:
                raise ValueError("订单状态不允许支付")
            
            # 检查是否已有未完成的支付记录
            existing_payment = Payment.query.filter_by(
                order_id=order_id,
                status=0  # 未支付状态
            ).first()
            
            # 如果存在未过期的支付记录，返回现有的
            if existing_payment:
                # 检查是否过期（创建时间 + 过期时间 > 当前时间）
                expire_time = existing_payment.created_at + timedelta(minutes=expire_minutes)
                if datetime.now() < expire_time:
                    # 返回现有支付记录的二维码
                    return self._generate_qr_response(existing_payment, order)
                else:
                    # 过期的支付记录标记为失败
                    existing_payment.status = 2
                    db.session.commit()
            
            # 创建新的支付记录
            payment = Payment(
                order_id=order_id,
                payment_no=self._generate_payment_no(),
                amount=order.amount,
                payment_method='wechat',
                status=0,  # 未支付
                created_at=datetime.now()
            )
            
            db.session.add(payment)
            db.session.flush()  # 获取payment.id
            
            # 生成微信支付订单
            wechat_order = self._create_wechat_order(
                payment=payment,
                order=order,
                description=description or f"场馆预约-{order.venue.name if order.venue else '未知场馆'}",
                expire_minutes=expire_minutes
            )
            
            # 记录支付日志
            self._log_payment_action(
                payment.id,
                'create_payment',
                1,
                {'payment_no': payment.payment_no, 'amount': float(payment.amount)},
                wechat_order
            )
            
            db.session.commit()
            
            return {
                'payment_id': payment.id,
                'payment_no': payment.payment_no,
                'qr_code': wechat_order.get('code_url'),
                'amount': float(payment.amount),
                'expire_time': (payment.created_at + timedelta(minutes=expire_minutes)).strftime('%Y-%m-%d %H:%M:%S')
            }
            
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"生成支付订单失败: {str(e)}")
            raise
    
    def _generate_qr_response(self, payment, order):
        """生成二维码响应数据"""
        # 这里应该重新获取或生成二维码URL
        # 简化处理，返回基本信息
        return {
            'payment_id': payment.id,
            'payment_no': payment.payment_no,
            'qr_code': f'weixin://wxpay/bizpayurl?pr={payment.payment_no}',  # 临时模拟
            'amount': float(payment.amount),
            'expire_time': (payment.created_at + timedelta(minutes=30)).strftime('%Y-%m-%d %H:%M:%S')
        }
    
    def _create_wechat_order(self, payment, order, description, expire_minutes):
        """创建微信支付订单"""
        config = self.config.get_wechat_config()
        
        # 构建请求参数
        params = {
            'appid': config['app_id'],
            'mch_id': config['mch_id'],
            'nonce_str': self._generate_nonce_str(),
            'body': description,
            'out_trade_no': payment.payment_no,
            'total_fee': int(payment.amount * 100),  # 转换为分
            'spbill_create_ip': '127.0.0.1',  # 实际应用中应该获取真实IP
            'notify_url': config['notify_url'],
            'trade_type': 'NATIVE',  # 扫码支付
            'time_expire': (datetime.now() + timedelta(minutes=expire_minutes)).strftime('%Y%m%d%H%M%S')
        }
        
        # 生成签名
        params['sign'] = self._generate_sign(params, config['api_key'])
        
        # 构建XML请求
        xml_data = self._dict_to_xml(params)
        
        # 发送请求到微信支付API
        url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'
        if config.get('is_sandbox'):
            url = 'https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder'
        
        try:
            response = requests.post(url, data=xml_data, headers={'Content-Type': 'application/xml'})
            response_data = self._xml_to_dict(response.text)
            
            if response_data.get('return_code') == 'SUCCESS':
                if response_data.get('result_code') == 'SUCCESS':
                    return response_data
                else:
                    raise Exception(f"微信支付错误: {response_data.get('err_code_des', '未知错误')}")
            else:
                raise Exception(f"微信支付通信错误: {response_data.get('return_msg', '未知错误')}")
                
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {str(e)}")
    
    def handle_notify(self, xml_data):
        """
        处理微信支付回调通知
        
        Args:
            xml_data: 微信发送的XML数据
            
        Returns:
            str: 回复给微信的XML响应
        """
        try:
            # 解析XML数据
            notify_data = self._xml_to_dict(xml_data)
            
            # 验证签名
            if not self._verify_sign(notify_data):
                return self._generate_notify_response('FAIL', '签名验证失败')
            
            # 检查通知状态
            if notify_data.get('return_code') != 'SUCCESS':
                return self._generate_notify_response('FAIL', '通知返回失败')
            
            if notify_data.get('result_code') != 'SUCCESS':
                return self._generate_notify_response('FAIL', '支付结果失败')
            
            # 获取支付记录
            payment_no = notify_data.get('out_trade_no')
            payment = Payment.query.filter_by(payment_no=payment_no).first()
            
            if not payment:
                return self._generate_notify_response('FAIL', '支付记录不存在')
            
            # 防重复处理
            if payment.status == 1:
                return self._generate_notify_response('SUCCESS', '已处理')
            
            # 验证金额
            total_fee = int(notify_data.get('total_fee', 0))
            if total_fee != int(payment.amount * 100):
                self._log_payment_action(
                    payment.id,
                    'notify_amount_mismatch',
                    0,
                    {'expected': int(payment.amount * 100), 'received': total_fee},
                    notify_data
                )
                return self._generate_notify_response('FAIL', '金额不匹配')
            
            # 更新支付记录
            payment.status = 1  # 支付成功
            payment.transaction_id = notify_data.get('transaction_id')
            payment.paid_at = datetime.now()
            
            # 更新订单状态
            order = payment.order
            if order:
                order.status = 1  # 已支付
                order.updated_at = datetime.now()
            
                # 创建入场记录
                self._create_checkin_record(order)
            
            # 记录支付日志
            self._log_payment_action(
                payment.id,
                'payment_success',
                1,
                {'transaction_id': payment.transaction_id},
                notify_data
            )
            
            db.session.commit()
            
            return self._generate_notify_response('SUCCESS', '处理成功')
            
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"处理支付回调失败: {str(e)}")
            return self._generate_notify_response('FAIL', f'处理失败: {str(e)}')
    
    def query_payment_status(self, payment_no):
        """查询支付状态"""
        config = self.config.get_wechat_config()
        
        params = {
            'appid': config['app_id'],
            'mch_id': config['mch_id'],
            'out_trade_no': payment_no,
            'nonce_str': self._generate_nonce_str()
        }
        
        params['sign'] = self._generate_sign(params, config['api_key'])
        xml_data = self._dict_to_xml(params)
        
        url = 'https://api.mch.weixin.qq.com/pay/orderquery'
        if config.get('is_sandbox'):
            url = 'https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery'
        
        try:
            response = requests.post(url, data=xml_data, headers={'Content-Type': 'application/xml'})
            return self._xml_to_dict(response.text)
        except Exception as e:
            current_app.logger.error(f"查询支付状态失败: {str(e)}")
            return None
    
    def _generate_payment_no(self):
        """生成支付单号"""
        timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
        random_str = str(uuid.uuid4().hex)[:6].upper()
        return f'PAY{timestamp}{random_str}'
    
    def _generate_nonce_str(self):
        """生成随机字符串"""
        return str(uuid.uuid4().hex)
    
    def _generate_sign(self, params, api_key):
        """生成签名"""
        # 排序参数
        sorted_params = sorted(params.items())
        # 拼接字符串
        string_a = '&'.join([f'{k}={v}' for k, v in sorted_params if v])
        string_sign_temp = f'{string_a}&key={api_key}'
        # MD5加密
        return hashlib.md5(string_sign_temp.encode('utf-8')).hexdigest().upper()
    
    def _verify_sign(self, params):
        """验证签名"""
        sign = params.pop('sign', '')
        calculated_sign = self._generate_sign(params, self.config.wechat_api_key)
        return sign == calculated_sign
    
    def _dict_to_xml(self, params):
        """字典转XML"""
        xml = '<xml>'
        for k, v in params.items():
            xml += f'<{k}><![CDATA[{v}]]></{k}>'
        xml += '</xml>'
        return xml
    
    def _xml_to_dict(self, xml_data):
        """XML转字典"""
        try:
            root = ET.fromstring(xml_data)
            return {elem.tag: elem.text for elem in root}
        except ET.ParseError:
            return {}
    
    def _generate_notify_response(self, status, message):
        """生成回调响应"""
        return f"""<xml>
<return_code><![CDATA[{status}]]></return_code>
<return_msg><![CDATA[{message}]]></return_msg>
</xml>"""
    
    def _create_checkin_record(self, order):
        """支付成功后创建入场记录"""
        try:
            from app.models.checkin_record import CheckinRecord
            
            # 检查是否已经创建过入场记录
            existing_record = CheckinRecord.query.filter_by(order_id=order.id).first()
            if existing_record:
                current_app.logger.info(f"订单 {order.id} 的入场记录已存在")
                return
            
            # 创建入场记录
            checkin_record = CheckinRecord.create_from_order(order)
            db.session.add(checkin_record)
            
            current_app.logger.info(f"为订单 {order.id} 创建入场记录成功")
            
        except Exception as e:
            current_app.logger.error(f"创建入场记录失败: {str(e)}")
    
    def _log_payment_action(self, payment_id, action, status, request_data=None, response_data=None):
        """记录支付日志"""
        import json
        
        log = PaymentLog(
            payment_id=payment_id,
            action=action,
            status=status,
            request_data=json.dumps(request_data, ensure_ascii=False) if request_data else None,
            response_data=json.dumps(response_data, ensure_ascii=False) if response_data else None,
            ip_address='127.0.0.1',  # 实际应用中应该获取真实IP
            user_agent='WeChatPayService/1.0',
            created_at=datetime.now()
        )
        
        db.session.add(log) 